package com.openthinks.joyniproject.dao;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.commons.lang.StringUtils;
import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcDaoSupport;
import org.springframework.jdbc.core.namedparam.SqlParameterSource;
import org.springframework.jdbc.core.simple.ParameterizedBeanPropertyRowMapper;
import org.springframework.jdbc.support.GeneratedKeyHolder;
import org.springframework.jdbc.support.KeyHolder;

import com.openthinks.joyniproject.exception.FrameException;
import com.openthinks.joyniproject.exception.NothingUpdateFrameException;
import com.openthinks.joyniproject.model.PageModel;
import com.openthinks.joyniproject.utils.Config;

public class BaseDao extends NamedParameterJdbcDaoSupport{
	/**
	 * 新增操作
	 * @param sql 执行的sql语句
	 * @param paramMap 使用Map传递多个参数值
	 * @return
	 */
	public int insert(String sql, Map<String,Object> paramMap) {
		int result = -1;
		
		try {
			result = getNamedParameterJdbcTemplate().update(sql, paramMap);
		} catch (Exception e) {
			logger.error(e.getMessage(), e);
			logger.error(sql);
			throw new FrameException("更新数据失败", e);
		}

		if (result >= 0) {
			return 1;
		} else {
			throw new NothingUpdateFrameException("未更新任何内容，如确实需要试图更新，请try catch此异常");
		}
	}
	/**
	 * 新增操作
	 * @param sql 执行的sql语句
	 * @param paramMap 使用Map传递多个参数值
	 * @param pk 主键
	 * @return
	 */
	public int insert(String sql, Map<String,Object> paramMap,String pk) {
		int result = -1;
		KeyHolder key = new GeneratedKeyHolder();
		
		try {
			SqlParameterSource sps = new MapSqlParameterSource(paramMap);
			
			result = getNamedParameterJdbcTemplate().update(sql, sps, key, new String[]{pk});
			
		} catch (Exception e) {
			logger.error(e.getMessage(), e);
			logger.error(sql);
			throw new FrameException("更新数据失败", e);
		}

		if (result > 0) {
			return (int)key.getKey().longValue();
		} else {
			throw new NothingUpdateFrameException("未更新任何内容，如确实需要试图更新，请try catch此异常");
		}
	}
	
	/**
	 * 修改操作
	 * @param sql 执行的sql语句
	 * @param paramMap 使用Map传递多个参数值
	 * @return
	 */
	public boolean update(String sql, Map<String,Object> paramMap) {
		return deleteOrUpdate(sql, paramMap);
	}
	
	/**
	 * 修改操作(批量执行)
	 * @param sql 执行的sql语句
	 * @param paramMaps 使用Map传递多个参数值
	 * @return
	 */
	public void batchUpdate(String sql, Map<String,Object>[] paramMaps) {
		
		batchDeleteOrUpdate(sql, paramMaps);
	}

	/**
	 * 删除操作
	 * @param sql 执行的sql语句
	 * @param paramMap 使用Map传递多个参数值
	 * @return
	 */
	public boolean delete(String sql, Map<String,Object> paramMap) {
		return deleteOrUpdate(sql, paramMap);
	}
	
	/**
	 * 删除操作
	 * @param sql 执行的sql语句
	 * @param paramMap 使用Map传递多个参数值
	 * @return
	 */
	public void batchDelete(String sql, Map<String,Object>[] paramMap) {
		batchDeleteOrUpdate(sql, paramMap);
	}
	
	/**
	 * 增改查操作，不对外开放，该方法对外提供insert,update,delete等接口以供调用
	 * @param sql 执行的sql语句
	 * @param params 可变参数或对象数组
	 * @return
	 */
	private boolean deleteOrUpdate(String sql, Map<String,Object> paramMap){
		int result = -1;
		
		try {
			result = getNamedParameterJdbcTemplate().update(sql, paramMap);
			
		} catch (Exception e) {
			logger.error(e.getMessage(), e);
			logger.error(sql);
			throw new FrameException("更新数据失败", e);
		}
		if (result > 0) {
			return true;
		} else {
			return false;
		}
	}
	
	/**
	 * 增改查操作(批量执行)，不对外开放，该方法对外提供update,delete等接口以供调用
	 * @param sql 执行的sql语句
	 * @param params 可变参数或对象数组
	 * @return
	 */
	private void batchDeleteOrUpdate(String sql, Map<String,Object>[] params){
		try {
			getNamedParameterJdbcTemplate().batchUpdate(sql, params);
			
		} catch (Exception e) {
			logger.error(e.getMessage(), e);
			logger.error(sql);
			throw new FrameException("更新数据失败", e);
		}
	}
	
	/**
	 * 计算总记录数
	 * @param sql
	 * 						原始SQL语句
	 * @param paramMap
	 * 						SQL参数
	 * @return
	 */
	public int queryTotalCount(String sql,  Map<String,Object> paramMap) {
		String totalCountSql = this.totalCountSql(sql);
		return this.queryForInt(totalCountSql, paramMap);
	}

	/**
	 * 根据条件查找列表，返回给定参数clz的pageModel
	 * 自动生成总页数sql语句,如果无特殊情况，优先考虑使用此方法分页查询
	 * @param sql 执行的sql语句
	 * @param clz 需要返回的对象类型
	 * @param start 开始的记录数
	 * @param limit 结束的记录数
	 * @param params 可变参数或对象数组
	 * @return
	 */
	@SuppressWarnings("rawtypes")
	public PageModel queryForPageModel(String sql, Class clz, Integer start, Integer limit, Map<String,Object> paramMap) {
		String totalCountSql = this.totalCountSql(sql);
		return this.queryForPageModel(sql, totalCountSql, clz, start, limit, paramMap);
	}

	/**
	 * 根据条件查找列表，返回给定参数clz的pageModel
	 * 手动传入总页数sql语句，
	 * @param sql 执行的sql语句
	 * @param totalCountSql 查询记录总数的sql语句
	 * @param clz 需要返回的对象类型
	 * @param start 开始的记录数
	 * @param limit 结束的记录数
	 * @param params 可变参数或对象数组
	 * @return
	 */
	@SuppressWarnings({ "rawtypes", "unchecked" })
	public PageModel queryForPageModel(String sql, String totalCountSql, Class clz, Integer start, Integer limit, Map<String,Object> paramMap) {
		PageModel pm = new PageModel();
		pm.setTotalRecords(this.queryForInt(totalCountSql, paramMap));//查询总记录数
		sql = this.paginateSql(sql, start, limit);//生成分页sql语句
		pm.setPageList(this.queryForList(sql, clz, paramMap));
		return pm;
	}
	
	/**
	 * 根据条件分页查询记录
	 * @param sql 执行的sql语句
	 * @param start 开始的记录数
	 * @param limit 结束的记录数
	 * @param paramsList 使用List传递多个参数值
	 * @return
	 */
	public PageModel queryForPageModel(String sql, Integer start, Integer limit, Map<String,Object> paramMap) {
		String totalCountSql = this.totalCountSql(sql);
		return queryForPageModel(sql, totalCountSql,start, limit, paramMap);
	}

	/**
	 * 根据条件分页查询记录，不返回记录总数，通过冗余字段获取总数
	 * @param sql 执行的sql语句
	 * @param start 开始的记录数
	 * @param limit 结束的记录数
	 * @param paramsList 使用List传递多个参数值
	 * @return
	 */
	public PageModel queryForPageModelWithoutTotalCount(String sql, Integer start, Integer limit, Map<String,Object> paramMap) {
		return queryForPageModel(sql, "",start, limit, paramMap);
	}
	
	/**
	 * 根据条件分页查询记录，不返回记录总数，通过冗余字段获取总数
	 * @param sql 执行的sql语句
	 * @param start 开始的记录数
	 * @param limit 结束的记录数
	 * @param paramsList 使用List传递多个参数值
	 * @return
	 */
	public <T> List<T> queryForObjectWithoutTotalCount(String sql,Class<T> clz, Integer start, Integer limit, Map<String,Object> paramMap) {
		sql = this.paginateSql(sql, start, limit);//生成分页sql语句
		return queryObjectForList(sql, clz, paramMap);
	}
	
	/**
	 * 根据条件分页查询记录
	 * @param sql 执行的sql语句
	 * @param totalCountSql 查询记录总数的sql语句
	 * @param start 开始的记录数
	 * @param limit 结束的记录数
	 * @param params 可变参数或对象数组
	 * @return
	 */
	public PageModel queryForPageModel(String sql, String totalCountSql, Integer start, Integer limit, Map<String,Object> paramMap) {
		PageModel pm = new PageModel();
		
		if(StringUtils.isNotEmpty(totalCountSql)){
			pm.setTotalRecords(this.queryForInt(totalCountSql, paramMap));//查询总记录数
		}

		sql = this.paginateSql(sql, start, limit);//生成分页sql语句

		pm.setPageList(this.queryForList(sql, paramMap));

		return pm;
	}
	
	/**
	 * 生成总记录数sql，如果此方法不能满足，请使用含有totalCountSql参数的方法自行传入sql
	 * @param orginSql 需要生成记录总数的sql语句
	 * @return
	 */
	private String totalCountSql(String orginSql) {
		String sql = orginSql.toLowerCase();
		//如果有group by语句,使用嵌套
		if (sql.indexOf(" group by ") > 0) {
			sql = "select count(*) from (" + orginSql + ")";
			//如果只有一个from,拼串
		} else if (sql.indexOf(" from ") == sql.lastIndexOf(" from ")){
			sql = "select count(*)" + orginSql.substring(orginSql.indexOf(" from "));
			//如果有多个from,使用嵌套
		} else {
			sql = "select count(*) from (" + orginSql + ")";
		}
		return sql;
	}

	/**
	 * 生成sql分页语句
	 * @param sql 需要分页的sql语句
	 * @param start 开始记录数
	 * @param end 结束记录数
	 * @return
	 */
	private String paginateSql(String sql, Integer start, Integer end) {
		String databaseType = Config.getProperty("database.type");
		
		if("mysql".equals(databaseType)){
			sql = "select t.* from ( " + sql +" ) t limit " + start.toString() + "," + end.toString();
		}else if("oracle".equals(databaseType)){
			sql = "select * from ( select rownum rn, t.* from ( " + sql
					+ " ) t where rownum <= " + end
					+ " ) where rn > " + start;
		}
		return sql;
	}
	
	/**
	 * 查询整型
	 * @param sql 执行的sql语句
	 * @param params 可变参数或对象数组
	 * @return
	 */
	public int queryForInt(String sql, Map<String,Object> paramMap) {
		try {
			return getNamedParameterJdbcTemplate().queryForInt(sql, paramMap);
		} catch (Exception e) {
			logger.error(e.getMessage(), e);
			logger.error(sql);
			throw new FrameException("查询数据失败", e);
		}
	}

	/**
	 * 查询长整型
	 * @param sql 执行的sql语句
	 * @param params 可变参数或对象数组
	 * @return
	 */
	public long queryForLong(String sql, Map<String,Object> paramMap) {
		try {
			return getNamedParameterJdbcTemplate().queryForLong(sql, paramMap);
		} catch (Exception e) {
			logger.error(e.getMessage(), e);
			logger.error(sql);
			throw new FrameException("查询数据失败", e);
		}
	}
	
	/**
	 * 根据条件查找列表，返回包含Map的List，Map键值对于数据库字段
	 * @param sql 执行的sql语句
	 * @param params 可变参数或对象数组
	 * @return
	 */
	public List<Map<String,Object>> queryForList(String sql, Map<String,Object> paramMap) {
		try {
			return this.keyLowerCase(getNamedParameterJdbcTemplate().queryForList(sql, paramMap));
		} catch (Exception e) {
			logger.error(e.getMessage(), e);
			logger.error(sql);
			throw new FrameException("查询数据失败", e);
		}
	}

	/**
	 * 根据条件查找列表，返回给定参数clz的对象List
	 * @param sql 执行的sql语句
	 * @param clz 需要返回的对象类型
	 * @param params 可变参数或对象数组
	 * @return
	 */
	public <T> List<T> queryForList(String sql, Class<T> clz, Map<String,Object> paramMap) {
		try {
			List<T> list = getNamedParameterJdbcTemplate().queryForList(sql, paramMap, clz);
			return list;
		} catch (EmptyResultDataAccessException e) {    
            return null;    
        }catch (Exception e) {
			logger.error(e.getMessage(), e);
			logger.error(sql);
			throw new FrameException("查询数据失败", e);
		}
	}
	
	/**
	 * 根据条件查找列表，返回给定参数clz的对象List
	 * @param sql 执行的sql语句
	 * @param clz 需要返回的对象类型
	 * @param params 可变参数或对象数组
	 * @return
	 */
	public <T> List<T> queryObjectForList(String sql, Class<T> clz, Map<String,Object> paramMap) {
		try {
			RowMapper<T> rm = ParameterizedBeanPropertyRowMapper.newInstance(clz);
			List<T> list = getNamedParameterJdbcTemplate().query(sql, paramMap, rm);
			return list;
		} catch (EmptyResultDataAccessException e) {    
            return null;    
        }catch (Exception e) {
			logger.error(e.getMessage(), e);
			logger.error(sql);
			throw new FrameException("查询数据失败", e);
		}
	}
	
	/**
	 * 根据条件查找列表，返回给定参数clz的对象List
	 * @param sql 执行的sql语句
	 * @param clz 需要返回的对象类型
	 * @param params 可变参数或对象数组
	 * @return
	 */
	public <T> List<T> queryForList(String sql, Class<T> clz, SqlParameterSource sqlParamSource) {
		try {
			List<T> list = getNamedParameterJdbcTemplate().queryForList(sql, sqlParamSource, clz);
			return list;
		} catch (EmptyResultDataAccessException e) {    
            return null;    
        }catch (Exception e) {
			logger.error(e.getMessage(), e);
			logger.error(sql);
			throw new FrameException("查询数据失败", e);
		}
	}
	
	/**
	 * 单个对象查询，返回给定参数clz的对象
	 * @param sql 执行的sql语句
	 * @param clz 需要返回的对象类型
	 * @param params 可变参数或对象数组
	 * @return
	 */
	public <T> T queryForObject(String sql, Class<T> clz, Map<String,Object> paramMap) {
		try {
			return getNamedParameterJdbcTemplate().queryForObject(sql, paramMap, ParameterizedBeanPropertyRowMapper.newInstance(clz));
		} catch (EmptyResultDataAccessException e) {    
            return null;    
        } catch (Exception e) {
			logger.error(e.getMessage(), e);
			logger.error(sql);
			throw new FrameException("查询数据失败", e);
		}
	}

	/**
	 * 单个对象查询，返回Map,键值对于数据库字段
	 * @param sql 执行的sql语句
	 * @param params 可变参数或对象数组
	 * @return
	 */
	public Map<String, Object> queryForMap(String sql, Map<String,Object> paramMap) {
		try {
			return this.keyLowerCase(getNamedParameterJdbcTemplate().queryForMap(sql, paramMap));
		} catch(EmptyResultDataAccessException em){
			return null;
		}catch (Exception e) {
			logger.error(e.getMessage(), e);
			logger.error(sql);
			throw new FrameException("查询数据失败", e);
		}
	}

	/**
	 * 将Map的key值全部转换成小写
	 * @param map
	 * @return
	 */
	private Map<String, Object> keyLowerCase(Map<String, Object> map) {
		Map<String, Object> newMap = new HashMap<String, Object>();
		Set<String> keySet = map.keySet();
		for (String key : keySet) {
			newMap.put(key.toLowerCase(), map.get(key));
		}
		map.clear();
		map = null;
		return newMap;
	}

	/**
	 * 将List中的Map的key值全部转换成小写
	 * @param map
	 * @return
	 */
	private List<Map<String,Object>> keyLowerCase(List<Map<String,Object>> list) {
		List<Map<String,Object>> newList = new ArrayList<Map<String,Object>>();
		for (Map<String,Object> map: list) {
			newList.add(keyLowerCase(map));
		}
		list.clear();
		list = null;
		return newList;
	}
}
